package com.link.base.base.user.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.link.base.base.accnt.DmpField;
import com.link.base.base.accnt.model.Account;
import com.link.base.base.accnt.service.AccountService;
import com.link.base.base.auth.model.Authclient;
import com.link.base.base.auth.service.AuthclientService;
import com.link.base.base.duty.model.Duty;
import com.link.base.base.duty.service.DutyService;
import com.link.base.base.enterprise.model.Enterprise;
import com.link.base.base.enterprise.service.EnterpriseService;
import com.link.base.base.mpconfig.model.Mp;
import com.link.base.base.mpconfig.service.MpService;
import com.link.base.base.mvg.dao.mybatis.user.mapper.UserDutyMvgMapper;
import com.link.base.base.mvg.dao.mybatis.user.mapper.UserPostnMvgMapper;
import com.link.base.base.mvg.model.Mvg;
import com.link.base.base.mvg.service.MvgService;
import com.link.base.base.orgnization.model.Orgnization;
import com.link.base.base.orgnization.service.OrgnizationService;
import com.link.base.base.passwdhistory.model.PasswdHistory;
import com.link.base.base.passwdhistory.service.PasswdHistoryService;
import com.link.base.base.position.model.Position;
import com.link.base.base.position.service.PositionService;
import com.link.base.base.user.dao.mybatis.mapper.LoginMapper;
import com.link.base.base.user.dao.mybatis.mapper.UserMapper;
import com.link.base.base.user.model.JWT;
import com.link.base.base.user.model.LoginDTO;
import com.link.base.base.user.model.RespDTO;
import com.link.base.base.user.model.User;
import com.link.base.core.RSAUtils.SecretKeyUtil;
import com.link.base.core.basic.interceptors.mybatis.system.KafkaSender;
import com.link.base.core.basic.util.DingDingUtil;
import com.link.base.microinterface.microservice.MemberClient;
import com.link.base.tencent.corpwx.agent.service.AgentService;
import com.link.base.user.model.AppSecurityMenu;
import com.link.base.user.model.CoreUser;
import com.link.base.user.model.RoleAuth;
import com.link.base.wechat.wxsubscriber.model.Subscriber;
import com.link.base.wechat.wxsubscriber.service.WxSubscriberService;
import com.link.core.basic.dao.mapper.BasicMapper;
import com.link.core.basic.interceptor.SecurityInterceptor;
import com.link.core.basic.model.BasicModel;
import com.link.core.basic.query.Filter;
import com.link.core.basic.query.QueryParams;
import com.link.core.basic.service.BasicServiceException;
import com.link.core.basic.service.BasicServiceImpl;
import com.link.core.basic.service.KeyGenerateService;
import com.link.core.basic.service.ServiceException;
import com.link.core.cllog.LCLogger;
import com.link.core.util.*;
import com.link.core.util.redisclient.LinkRedisClient;
import com.link.microservice.AuthClient;
import feign.FeignException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.jetty.util.StringUtil;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author unknown
 * @date unknown
 * @comments 用户数据服务
 */
@Service
public class UserServiceImpl extends BasicServiceImpl<User> implements UserService {

    private static final Logger logger = LogManager.getLogger(UserServiceImpl.class);

    private static final String CRM_ENTRANCE = "crm";
    private static final String DMS_ENTRANCE = "dms";
    private static final String API_ENTRANCE = "api";
    private static final String WECHAT_ENTRANCE = "wechat";
    private static final String CORPWX_MP_ENTRANCE = "corpwxmp";

    /**
     * 经销商用户
     */
    private static final String DEALER_EMP = "DealerEmployee";

    /**
     * 门店用户
     */
    private static final String TERM_EMP = "TerminalEmployee";

    /**
     * 品牌商用户
     */
    private static final String VEN_EMP = "VendorEmployee";

    /**************** 自动登录token管理-start*************/

    /**
     * loginToken超时时间 为2小时, 7200s
     */
    private static final Integer ACCESS_TOKEN_EXPIRE_TIME = 7200;

    private static final String SUPERSET_TOKEN_KEY = "superset";

    @Resource
    public UserPostnMvgMapper userPostnMvgMapper;
    @Resource
    public UserDutyMvgMapper userDutyMvgMapper;
    @Resource
    protected KeyGenerateService keyGenerateService;
    @Resource
    private UserMapper userMapper;
    @Resource
    private LoginMapper loginMapper;
    @Resource
    private PositionService positionService;
    @Resource
    private DutyService dutyService;
    @Resource
    private OrgnizationService orgnizationService;
    @Resource
    private MvgService mvgService;
    @Resource
    private EnterpriseService enterpriseService;
    @Resource
    private PasswdHistoryService passwdHistoryService;
    @Resource
    private Environment env;
    @Resource
    private LoginService loginService;

    @Resource
    private AccountService accountService;

    @Resource
    private WxSubscriberService wxSubscriberService;

    @Resource
    private AuthclientService authclientService;

    @Resource
    private MpService mpService;

    @Resource
    private AuthClient authClient;

    @Resource
    private MemberClient memberClient;

    @Resource
    private AgentService agentService;



    @Override
    public BasicMapper<User> getBasicMapper() throws Exception {
        return userMapper;
    }

    /**
     * 修改员工密码
     *
     * @param id              员工ID
     * @param newPassword     新密码
     * @param confirmPassword 确认密码
     * @return String
     * @throws BasicServiceException
     */
    @Override
    public String modifyEmpLoyeePassword(Long id, String newPassword, String confirmPassword) throws BasicServiceException {
        if (newPassword != null && !newPassword.trim().isEmpty()) {
            if (confirmPassword != null && !confirmPassword.trim().isEmpty()) {
                newPassword = newPassword.trim();
                confirmPassword = confirmPassword.trim();
                if (newPassword.equals(confirmPassword)) {
                    CoreUser sessionUser = UserUtil.getUser();
                    if (!sessionUser.getId().equals(id)) {
                        //修改密码信息
                        // passwordEncoder.encodePassword(newPassword,null);
                        String encodedNewPassword = PasswordUtil.generatePwd(newPassword);
                        User record = new User();
                        record.setId(id);
                        record.setPassword(encodedNewPassword);
                        try {
                            // 判断新密码是否与原密码一致
                            User u = new User();
                            u.setId(id);
                            User rs = userMapper.queryByUserId(u);

                            //校验密码
                            //校验历史密码
                            Enterprise enterprise = enterpriseService.queryByCode(rs.getCorpid());
                            if ("Y".equals(enterprise.getForceHistyPw())) {
                                // 如果强制历史密码为Y，则校验是否存在历史密码
                                int hisNum = enterprise.getPwKeepDate();
                                boolean passwdSameFlag = passwdHistoryService.checkHistory(newPassword, hisNum, rs.getUsername());
                                String refuse = "refuse";
                                if (!passwdSameFlag && refuse.equals(enterprise.getPwCheckGrade())) {
                                    throw new BasicServiceException("新密码不能与历史密码一致");
                                }
                            }
                            PasswordUtil.checkPasswd(newPassword, enterprise, rs);

                            userMapper.changeUserPassword(record);

                            //把修改的记录写入历史表
                            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                            Date date = new Date();
                            String changePwDate = sdf.format(date);

                            PasswdHistory passwdHistory = new PasswdHistory();
                            passwdHistory.setPassword(encodedNewPassword);
                            passwdHistory.setUserName(rs.getUsername());
                            passwdHistory.setModifyDateTime(changePwDate);
                            passwdHistoryService.insert(passwdHistory);
                        } catch (Exception e) {
                            LCLogger.withException(e);
                            throw new BasicServiceException(e.getMessage());
                        }
                    } else {
                        throw new BasicServiceException("不能修改自己的密码，请通过快捷栏修改自己的密码");
                    }

                } else {
                    throw new BasicServiceException("新密码与确认密码不一致");
                }
            } else {
                throw new BasicServiceException("确认密码为空");
            }
        } else {
            throw new BasicServiceException("新密码为空");
        }

        return null;
    }

    /**
     * 修改员工密码
     */
    @Override
    public void updatePassword(User record) throws Exception {
        try {
            userMapper.changeUserPassword(record);
        } catch (Exception e) {
            LCLogger.withException(e);
        }

    }

    @Override
    public String modifyPassword(CoreUser user, String oldPassword, String newPassword, String confirmPassword)
            throws BasicServiceException {
        if (oldPassword != null && !oldPassword.trim().isEmpty()) {
            if (newPassword != null && !newPassword.trim().isEmpty()) {
                if (confirmPassword != null && !confirmPassword.trim().isEmpty()) {
                    oldPassword = oldPassword.trim();
                    newPassword = newPassword.trim();
                    confirmPassword = confirmPassword.trim();
                    try {
                        User u = new User();
                        u.setId(user.getId());
                        User rs = userMapper.queryByUserId(u);
                        user.setPassword(rs.getPassword());
                        // passwordEncoder.encodePassword(oldPassword,null);
                        String encodedOldPassword = PasswordUtil.generatePwd(oldPassword);
                        if (user.getPassword().equals(encodedOldPassword)) {
                            if (newPassword.equals(confirmPassword)) {
                                // 修改密码信息
                                // passwordEncoder.encodePassword(newPassword,null);
                                String encodedNewPassword = PasswordUtil.generatePwd(newPassword);
                                user.setPassword(encodedNewPassword);

                                // 校验密码
                                // 校验历史密码
                                Enterprise enterprise = enterpriseService.queryByCode(user.getCorpid());
                                if ("Y".equals(enterprise.getForceHistyPw())) {
                                    // 如果强制历史密码为Y，则校验是否存在历史密码
                                    int hisNum = enterprise.getPwKeepDate();
                                    boolean passwdSameFlag = passwdHistoryService.checkHistory(newPassword, hisNum,
                                            rs.getUsername());
                                    String refuse = "refuse";
                                    if (!passwdSameFlag && refuse.equals(enterprise.getPwCheckGrade())) {
                                        throw new BasicServiceException("新密码不能与历史密码一致");
                                    }
                                }
                                PasswordUtil.checkPasswd(newPassword, enterprise, user);
                                userMapper.changeUserPassword(user);

                                // 把修改的记录写入历史表
                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                                Date date = new Date();
                                String changePwDate = sdf.format(date);

                                PasswdHistory passwdHistory = new PasswdHistory();
                                passwdHistory.setPassword(encodedNewPassword);
                                passwdHistory.setUserName(user.getUsername());
                                passwdHistory.setModifyDateTime(changePwDate);
                                passwdHistoryService.insert(passwdHistory);

                            } else {
                                throw new BasicServiceException("新密码与确认密码不一致");
                            }
                        } else {
                            throw new BasicServiceException("原密码错误");
                        }
                    } catch (Exception e) {
                        LCLogger.withException(e);
                        throw new BasicServiceException(e.getMessage());
                    }
                } else {
                    throw new BasicServiceException("确认密码为空");
                }
            } else {
                throw new BasicServiceException("新密码为空");
            }
        } else {
            throw new BasicServiceException("原密码为空");
        }

        return null;
    }

    @Override
    public Mp getMpConfigInfo(LinkRedisClient jedis, String appEncryptionId) throws Exception {
        String mpAppId = null;
        String mpAppSecret = null;
        String mpCorpId = null;
        if (StringUtils.isBlank(appEncryptionId)) {
            throw new Exception("请传入appEncryptionId!");
        }
        // 根据前端传入的加密id查询
        Mp mp = new Mp();
        mp.setAppEncryptionId(appEncryptionId);
        mp = mpService.queryMpInfoByEncryptionId(mp);
        return mp;
    }

    @Override
    public JSONObject getJsCodeToSession(String code,String mpAppId,String mpAppSecret) throws Exception {
        //根据code获取openId
        String params = "appid=" + mpAppId + "&secret="
                + mpAppSecret + "&js_code=" + code + "&grant_type=authorization_code";
        LCLogger.info().withMessageKey("mpLogin")
                .withMessage("传递参数 : " + params).flush();


        String resultParams = HttpUtils.sendPost("https://api.weixin.qq.com/sns/jscode2session", params);
        LCLogger.info().withMessageKey("mpLogin")
                .withMessage("返回参数 : " + resultParams).flush();
        return JSONObject.parseObject(resultParams);
    }

    @Override
    public JSONObject codeToSessionOfCorpWx(String code,String accessToken) throws Exception {
        //根据code获取openId
        String params = "access_token=" + accessToken + "&js_code=" + code + "&grant_type=authorization_code";
        LCLogger.info().withMessageKey("codeToSessionOfCorpWx")
                .withMessage("传递参数 : " + params).flush();

        String resultParams = HttpUtils.sendPost("https://qyapi.weixin.qq.com/cgi-bin/miniprogram/jscode2session", params);
        LCLogger.info().withMessageKey("codeToSessionOfCorpWx")
                .withMessage("返回参数 : " + resultParams).flush();
        return JSONObject.parseObject(resultParams);
    }

    /**
     * 重置密码
     *
     * @param user
     * @param newPassword
     * @param confirmPassword
     * @return String
     * @throws BasicServiceException
     */
    @Override
    public String resetPassword(User user, String newPassword,
                                String confirmPassword) throws BasicServiceException {
        if (newPassword != null && !newPassword.trim().isEmpty()) {
            if (confirmPassword != null && !confirmPassword.trim().isEmpty()) {
                newPassword = newPassword.trim();
                confirmPassword = confirmPassword.trim();
                if (newPassword.equals(confirmPassword)) {
                    int len = 6;
                    if (newPassword.length() >= len) {
                        //修改密码信息
                        //passwordEncoder.encodePassword(newPassword,null);
                        String encodedNewPassword = PasswordUtil.generatePwd(newPassword);
                        user.setPassword(encodedNewPassword);
                        try {
                            userMapper.resetUserPassword(user);
                        } catch (Exception e) {
                            LCLogger.withException(e);
                            throw new BasicServiceException(e.getMessage());
                        }
                    } else {
                        throw new BasicServiceException("密码长度不足6位");
                    }
                } else {
                    throw new BasicServiceException("新密码与确认密码不一致");
                }
            } else {
                throw new BasicServiceException("确认密码为空");
            }
        } else {
            throw new BasicServiceException("新密码为空");
        }


        return null;
    }

    /**
     * 根据用户名查找用户
     *
     * @param username 用户名
     * @return 用户信息
     */
    @Override
    public User getUserByUsername(String username) throws BasicServiceException {
        //忽略大小写,自动转换成大写
        String usernameUpperCase = username.toUpperCase();
        User record = new User();
        record.setUsername(usernameUpperCase);
        try {
            return loginMapper.queryByUsername(record);
        } catch (Exception e) {
            LCLogger.withException(e);
            throw new BasicServiceException(e.getMessage());
        }
    }

    @Override
    public User querySampleUserById(User record) throws Exception {
        return userMapper.querySampleUserById(record);
    }

    @Override
    public void updateDmsProfile(User record) throws Exception {
        userMapper.updateDmsProfile(record);
    }

    @Override
    @Transactional(rollbackFor = BasicServiceException.class)
    public void upsert(User entity) throws Exception {
        super.upsert(entity);
        //同时修改职位的主要负责人
        Long postnId = entity.getPostnId();
        Long userId = entity.getId();
        if (postnId != null) {
            QueryParams qps = new QueryParams();
            qps.setPageFlag(false);
            qps.addFilter(new Filter("id", String.valueOf(postnId)));
            Mvg mvgParam = (Mvg) BasicModel.transformClass(Mvg.class, qps);
            mvgParam.setMvgParentId(userId);
            List<HashMap<String, String>> postnMapList = userPostnMvgMapper.queryRightListPage(mvgParam);
            if (postnMapList.size() == 1) {
                positionService.mainUserUpdate(postnId, userId);
            }
        }
    }

    @Override
    public User outerPreDefaultValue() throws Exception {
        User outerUser = new User();
        Long outerUserId = keyGenerateService.keyGenerate();
        String defaultDutyId = PropertyUtil.getProperty(RedisConstants.key_outerUserDefaultDuty, outerUser.getLoginCorpId());
        String defaultPostnId = PropertyUtil.getProperty(RedisConstants.key_outerUserDefaultPostn, outerUser.getLoginCorpId());
        String defaultRole = PropertyUtil.getProperty(RedisConstants.key_outerUserDefaultRole, outerUser.getLoginCorpId());
        outerUser.setId(outerUserId);
        outerUser.setRole(defaultRole);
        outerUser.setDutyId(Long.valueOf(defaultDutyId));
        outerUser.setPostnId(Long.valueOf(defaultPostnId));
        Mvg mvg = new Mvg();
        mvg.setMvgParentId(outerUserId);
        if (defaultDutyId != null) {
            //设置职责mvg默认值
            Duty duty = new Duty();
            duty.setId(Long.valueOf(defaultDutyId));
            duty = dutyService.queryById(duty);
            if (duty != null) {
                outerUser.setDutyName(duty.getName());
                mvg.setId(keyGenerateService.keyGenerate());
                mvg.setMvgSubsetId(Long.valueOf(defaultDutyId));
                userDutyMvgMapper.insert(mvg);
            }
        }
        if (defaultPostnId != null) {
            //设置职位mvg默认值
            Position postn = new Position();
            postn.setId(Long.valueOf(defaultPostnId));
            postn = positionService.queryById(postn);
            if (postn != null) {
                outerUser.setPostnName(postn.getPostnName());
                mvg.setId(keyGenerateService.keyGenerate());
                mvg.setMvgSubsetId(Long.valueOf(defaultPostnId));
                userPostnMvgMapper.insert(mvg);
            }
        }
        return outerUser;
    }

    @Override
    public void mainPostnUpdate(Long userId, Long postnId) throws Exception {
        User entity = new User();
        entity.setId(userId);
        entity.setPostnId(postnId);
        userMapper.mainPostnUpdate(entity);
    }

    @Override
    public void copyUserMvgInfo(User user, QueryParams qps) throws Exception {
        qps.setMvgParentId(user.getCorpid());
        qps.setPageFlag(false);
        qps.setTotalFlag(true);
        qps.setUserOrgId(user.getOrgId());
        qps.setUserPositionId(user.getPostnId());

        qps.setMvgMapperName("userDuty");
        mvgService.setMvgMapperName(qps.getMvgMapperName());
        Mvg dutyModel = (Mvg) BasicModel.transformClass(Mvg.class, qps);
        List<HashMap<String, String>> dutyList = mvgService.queryRightListPage(dutyModel);
        for (int i = 0; i < dutyList.size(); i++) {
            Duty duty = (Duty) ((Object) (dutyList.get(i)));

            Mvg mvg = new Mvg();
            Long id = keyGenerateService.keyGenerate();
            mvg.setId(id);
            mvg.setMvgParentId(user.getId());
            mvg.setMvgSubsetId(duty.getId());
            mvgService.insert(mvg);
        }

        qps.setMvgMapperName("userPostn");
        mvgService.setMvgMapperName(qps.getMvgMapperName());
        Mvg postnModel = (Mvg) BasicModel.transformClass(Mvg.class, qps);
        List<HashMap<String, String>> postnList = mvgService.queryRightListPage(postnModel);
        for (int i = 0; i < postnList.size(); i++) {
            Position postn = (Position) ((Object) (postnList.get(i)));

            Mvg mvg = new Mvg();
            Long id = keyGenerateService.keyGenerate();
            mvg.setId(id);
            mvg.setMvgParentId(user.getId());
            mvg.setMvgSubsetId(postn.getId());
            mvgService.insert(mvg);
        }
    }

    @Override
    public List<User> queryCorpSampleUser(User record) throws Exception {

        return userMapper.queryCorpSampleUser(record);
    }

    /**
     * 根据用户Id查找用户
     *
     * @return 用户信息
     */
    @Override
    public User getUserByUserId(User user) throws Exception {
        return userMapper.queryByUserId(user);
    }

    /***
     * DESC: 密码规则校验
     *
     * @param user     用户
     * @param password 密码
     * @author: xubowen
     * @return: void
     * @date: 2019-05-05 11:54
     */
    private RespDTO checkPassword(User user, String password, Enterprise enterprise) throws Exception {

        RespDTO respDTO = new RespDTO();

        String username = user.getUsername();
        //查询数据库中密码,比对
        User record = new User();
        record.setUsername(username);
        record.setLogin(username);
        User user1 = userMapper.queryByLogin(record);
        String truePw = user1.getPassword();

        //1.密码是否正确
        if (!StringUtils.equals(truePw, PasswordUtil.generatePwd(password))) {
            //设置错误次数
            setLoginErrorTimes(username, getLoginErrorTimes(username) + 1);
            throw new BasicServiceException("用户名或密码错误，请重新输入");
        }

        Integer pwExpireDate = enterprise.getPwExpireDate();

        //2.密码是否过期,未配置pwKeepDate则不校验
        if (pwExpireDate != null && pwExpireDate > 0) {
            //上次修改密码时间
            String lastPwChangeDateStr = user1.getLastPwChangeDate();
            //校验密码过期的开始时间。有修改记录则为上次密码修改时间。无则为建号时间
            String checkStartDateStr;
            //有修改记录
            if (StringUtils.isNotBlank(lastPwChangeDateStr)) {
                checkStartDateStr = lastPwChangeDateStr;
            } else {
                //无修改记录
                checkStartDateStr = user1.getCreated();
            }
            Date date = DateUtil.stringToDate(checkStartDateStr, "yyyy-MM-dd HH:mm:ss");
            Date expireDate = DateUtil.addMonthToDate(date, pwExpireDate);
            Date currentDate = new Date();
            //checkStartDateStr+过期时间<当前，则需要修改密码
            if (expireDate.before(currentDate)) {
                //标记需跳转至密码修改界面
                respDTO.setCode(-2);
                respDTO.setError("密码已过期,请修改密码再试");
                return respDTO;
            }
        }

        String refuse = "refuse";
        //3.密码是否符合规范 校验强度为 refuse时才进行校验
        if (StringUtils.isNotBlank(enterprise.getPwGrade())
                && StringUtils.isNotBlank(enterprise.getPwCheckGrade())
                && StringUtils.equals(refuse, enterprise.getPwCheckGrade())) {

            enterprise.setPwGrade(enterprise.getPwGrade());
            enterprise.setPwCheckGrade(enterprise.getPwCheckGrade());
            try {
                PasswordUtil.checkPasswd(password, enterprise, user);
            } catch (Exception e) {
                //标记需跳转至密码修改界面
                respDTO.setCode(-2);
                respDTO.setError("当前密码不规范:" + e.getMessage());
                return respDTO;
            }
        }

        return null;
    }

    /**
     * 设置登录认证后相关的信息
     *
     * @param token        认证成功后的toke
     * @param user         当前登录用户
     * @param refreshToken 刷新token
     * @param expires      token有效时间
     * @throws Exception
     * @author heqiqian
     * @date 2018年4月14日
     */
    @Override
    public void setLoginInfo(String token, User user, String refreshToken, int expires, String scope, Enterprise enterpriseArg) throws Exception {
        if (StringUtils.isBlank(token)) {
            throw new BasicServiceException("token不能为空");
        }
        if (user == null) {
            throw new BasicServiceException("user不能为空");
        }

        //查询出当前用户的所有职责，存入对应的字段里面去，在权限校验的时候使用
        Mvg mvg = new Mvg();
        mvg.setPageFlag(false);
        mvg.setTotalFlag(true);
        mvg.setMvgMapperName("userDuty");
        mvg.setMvgParentId(user.getId());
        mvgService.setMvgMapperName("userDuty");
        List<HashMap<String, String>> dutyListMap = mvgService.queryRightListPage(mvg);
        String dutyListStr = JSON.toJSONString(dutyListMap);
        List<Duty> dutyIdList = JSON.parseArray(dutyListStr, Duty.class);
        List<Long> userDutyIdList = new ArrayList<>();
        for (Duty duty : dutyIdList) {
            userDutyIdList.add(duty.getId());
        }
        user.setDutyIdList(userDutyIdList);
        LinkRedisClient jedis = null;
        try {
            jedis = RedisUtil.getJedis();
            // 防止转换为json报错
            user.setAnonymousFlag("Y");

            if (StringUtils.isBlank(user.getLoginTime())) {
                user.setLoginTime(DateUtil.dateToStr(new Date(), "yyyy-MM-dd HH:mm:ss"));
            }
            String userJson = JsonUtil.toString(user);

            String tokenKey = SecurityInterceptor.TOKEN_PREFIX + token;
            jedis.setex(tokenKey, expires, userJson);
            SecurityInterceptor.tempUser.set(user);
            Enterprise enterprise = enterpriseArg;
            if (enterprise == null) {
                //如果传入的企业参数为空，则重新获取
                enterprise = enterpriseService.queryByCode(user.getCorpid());
            }
            SecurityInterceptor.tempEnterpirse.set(enterprise);
            String enterpriseJson = JsonUtil.toString(enterprise);
            SessionUtil.setAttribute(SecurityInterceptor.LOGIN_INFO_ENTERPRISE_FIELD, enterpriseJson, expires);
            SessionUtil.setAttribute(SecurityInterceptor.ACCESS_TOKEN_FIELD, token, expires);
            SessionUtil.setAttribute(SecurityInterceptor.TOKEN_SCOPE, scope, expires);
        } finally {
            RedisUtil.returnResource(jedis);
        }
    }


    /**
     * 获取某用户名的登录错误次数
     *
     * @param username 用户名
     * @return errorTimes 规定时间内的密码错误次数
     */
    @Override
    public int getLoginErrorTimes(String username) throws Exception {
        int errorTimes = 0;
        LinkRedisClient jedis = null;
        try {
            jedis = RedisUtil.getJedis();
            String redisKey = "crm_loginError_info";
            if (jedis.exists(redisKey) && jedis.hmget(redisKey, username).size() > 0) {
                String str = jedis.hmget(redisKey, username).get(0);
                if (str != null) {
                    errorTimes = Integer.parseInt(str.split("\\++")[0]);
                }
            }

        } finally {
            RedisUtil.returnResource(jedis);
        }
        return errorTimes;
    }

    /**
     * 校验用户名对应的登录错误次数
     *
     * @param username
     */
    @Override
    public void checkLoginErrorTimes(String username) {
        LinkRedisClient jedis = RedisUtil.getJedis();
        try {
            String redisKey = "crm_loginError_info";
            Map<String, String> map = new HashMap<String, String>();
            // 记录当前登录时间
            String currentdate = DateUtil.dateToStr(new Date(), "yyyy-MM-dd HH:mm:ss");

            List<String> fieldList = jedis.exists(redisKey) ? jedis.hmget(redisKey, username) : null;
            if (fieldList != null && fieldList.size() > 0 && fieldList.get(0) != null) {
                String infoStr = fieldList.get(0);
                String str = "\\+";
                if (Integer.parseInt(infoStr.split(str)[0]) >= User.MAX_LOGIN_ERROR_TIME) {
                    Date currentime = new Date();
                    Date lasttime = DateUtil.stringToDate(infoStr.split("\\++")[1], "yyyy-MM-dd HH:mm:ss");
                    int num = 1000 * 60 * 60;
                    int two = 2;
                    if ((currentime.getTime() - lasttime.getTime()) / num < two) {
                        throw new RuntimeException("用户名密码错误次数过多，系统被锁定");
                    } else {
                        jedis.hdel(redisKey, username);
                        map.put(username, "0" + "+" + currentdate);
                        jedis.hmset(redisKey, map);
                    }
                }
            } else {
                map.put(username, 0 + "+" + currentdate);
                jedis.hmset(redisKey, map);
            }
        } catch (Exception e) {
            throw e;
        } finally {
            RedisUtil.returnResource(jedis);
        }
    }

    /**
     * author Li Yang
     * 获取token的方式改为直接拿登录时得到的token
     * lastupdated 2018-08-02
     */
    @Override
    public void bindingUserAndToken(User user, String accessToken, int expire) throws Exception {
        if (!StringUtils.isBlank(accessToken)) {
            user.setUserToken(accessToken);
            //将用到的user属性作为value
            JSONObject jsonUser = new JSONObject();
            jsonUser.put("link_userid", user.getId());
            jsonUser.put("link_username", user.getUsername());
            jsonUser.put("link_fstname", user.getFirstName());
            jsonUser.put("link_posid", user.getPostnId());
            jsonUser.put("link_posname", user.getPostnName());
            jsonUser.put("link_orgid", user.getOrgId());
            jsonUser.put("link_orgname", user.getOrgName());
            jsonUser.put("username", user.getUsername());
            jsonUser.put("link_corpid", user.getCorpid());

            String value = JSONObject.toJSONString(jsonUser, SerializerFeature.WriteMapNullValue);
            //将这个键值对放入redis中
            SessionUtil.setAttribute(SUPERSET_TOKEN_KEY, value, expire);
        } else {
            LCLogger.info().withMessageKey("bindingUserAndToken")
                    .withMessage("未获取到认证服务器返回的token...").flush();
        }
    }

    @Override
    public User queryByUserName(String username) {
        User record = new User();
        record.setUsername(username);
        record.setLogin(username);
        User user = userMapper.queryByLogin(record);

        if ((user == null)) {
            return null;
        }
        /* 职责 */
        String resp = user.getAppResp();
        if (resp != null && !"".equals(resp)) {
            // 查询用户菜单列表
            List<RoleAuth> list = userMapper.queryRoleAuth(user);
            List<AppSecurityMenu> appSecurityMenuList = null;
            if (list != null && list.size() > 0) {
                appSecurityMenuList = userMapper.querySecurityMenu(list);

                if (appSecurityMenuList != null && appSecurityMenuList.size() > 0) {
                    for (RoleAuth auth : list) {
                        if ("menu".equals(auth.getType())) {
                            List<AppSecurityMenu> tempAppSecurityMenuList = new ArrayList<>();
                            for (AppSecurityMenu item : appSecurityMenuList) {
                                if (item.getParentId().equals(auth.getId())) {
                                    tempAppSecurityMenuList.add(item);
                                }
                            }
                            auth.setSecurityMenuList(tempAppSecurityMenuList);
                        }
                    }
                }
            }
            user.setList(list);
        }
        return user;
    }

    /**
     * 自动登录-校验用户登录Token合法性
     *
     * @param login
     * @param UUID
     * @param loginToken
     * @return loginToken
     * @throws Exception
     * @author ouyangzhenqiang
     */
    @Override
    public String verifyLoginToken(String login, Long corpId, String UUID, String loginToken) throws Exception {
        LCLogger.info().withMessageKey("verifyLoginToken")
                .withMessage("verifyLoginToken=> loginToken: "
                        + loginToken + ",login: " + login + ",UUID: " + UUID).flush();
        if (StringUtils.isNotBlank(loginToken) && StringUtils.isNotBlank(login) && StringUtils.isNotBlank(UUID)) {
            LinkRedisClient jedis = null;
            try {
                jedis = RedisUtil.getJedis();
                String redisKey = login + "_" + corpId + "_" + UUID;
                String redisLogin = jedis.hget(redisKey, "login");
                String redisUUID = jedis.hget(redisKey, "UUID");
                String redisLoginToken = jedis.hget(redisKey, "login_token");
                String refresh_token = jedis.hget(redisKey, "refresh_token");
                String access_token = "";
                if (!login.equals(redisLogin) || !UUID.equals(redisUUID) || !loginToken.equals(redisLoginToken)) {
                    RedisUtil.returnResource(jedis);
                    return "";
                } else {
                    Long redisLoginTokenTime = Long.valueOf(jedis.hget(redisKey, "login_token_time"));
                    Long currentTime = System.currentTimeMillis();
                    long num = 1000;
                    if (((currentTime - redisLoginTokenTime)) >= num * ACCESS_TOKEN_EXPIRE_TIME) {
                        LCLogger.info().withMessageKey("verifyLoginToken")
                                .withMessage("loginToken有效期已超2小时，开始刷新...").flush();
                        String redisRefreshLoginToken = jedis.hget(redisKey, "login_refresh_token");
                        String newloginToken = PasswordUtil.md5(login + redisRefreshLoginToken);
                        jedis.hset(redisKey, "login_token", newloginToken);

                        String url = env.getProperty("auth.token_url") + "?grant_type=refresh_token";
                        JSONObject jsonO = new JSONObject();
                        jsonO.put("client_id", "app");
                        jsonO.put("client_secret", "HQ8H*wzpnK3@");
                        jsonO.put("refresh_token", refresh_token);
                        Map<String, Object> result = HttpRequestUtil.httpPost(url, jsonO, false, env.getProperty("auth.authorization"));
                        Boolean success = (Boolean) result.get("success");
                        if (success) {
                            String ret = result.get("message").toString();
                            if (StringUtils.isNull(ret)) {
                                throw new BasicServiceException("认证错误！");
                            }
                            JSONObject json = JSONObject.parseObject(ret);
                            JWT jwt = JSONObject.toJavaObject(json, JWT.class);
                            access_token = jwt.getAccess_token();
                            jedis.hset(redisKey, "access_token", access_token);
                            LCLogger.info().withMessageKey("verifyLoginToken")
                                    .withMessage("verifyLoginToken=> newloginToken: " + newloginToken).flush();
                            return newloginToken;
                        } else {
                            throw new BasicServiceException(result.get("message").toString());
                        }
                    } else {
                        LCLogger.info().withMessageKey("verifyLoginToken")
                                .withMessage("verifyLoginToken=> oldloginToken: " + loginToken).flush();
                        return loginToken;
                    }
                }
            } finally {
                RedisUtil.returnResource(jedis);
            }
        } else {
            return "";
        }
    }

    @Override
    public String mobileGetLoginToken(String login, Long corpId, String UUID, String password, String scope) throws Exception {
        LCLogger.info().withMessageKey("getLoginToken")
                .withMessage("getLoginToken=> login: " + login + ",corpId: " + corpId + ",UUID: " + UUID).flush();
        if (StringUtils.isNotBlank(login) && corpId != null && StringUtils.isNotBlank(UUID)) {
            LinkRedisClient jedis = null;
            try {
                jedis = RedisUtil.getJedis();
                int expireDays = 0;
                try {
                    // login_token失效时间，单位为天
                    expireDays = Integer.parseInt(RedisUtil.getProperty("app_login_token_time_limit", String.valueOf(corpId)));
                } catch (Exception e) {
                    LCLogger.error().withMessageKey("getLoginToken")
                            .withMessage("app_login_token_time_limit参数获取异常，暂时默认为1，请检查系统参数配置.").flush();
                    expireDays = 1;
                }
                // login相关信息key
                String redisKey = login + "_" + corpId + "_" + UUID;
                jedis.del(redisKey);
                String refresh_token;
                String access_token;
                int expiresIn;
                Map<String, String> jsonParam = new HashMap<>();
                jsonParam.put("username", login);
                jsonParam.put("password", password);
                jsonParam.put("scope", scope);
                jsonParam.put("grant_type", "password");
                Authclient authclient = getAuthclient(scope, "");
                String authHeader = genAuthHeader(authclient);
                if (StringUtils.isNull(password)) {
                    // 传入的密码为空，则表示当前为第三方登录
                    jsonParam.put("thirdName", "admin");
                    jsonParam.put("thirdPassword", "appThirdLogin");
                }
                String retStr;
                try {
                    retStr = authClient.getToken(authHeader, jsonParam);
                } catch (FeignException e) {
                    throw new BasicServiceException(e.getMessage());
                } catch (Exception e2) {
                    throw new BasicServiceException("登录错误，请联系管理员处理！");
                }
                JSONObject json = JSONObject.parseObject(retStr);
                JWT jwt = JSONObject.toJavaObject(json, JWT.class);
                refresh_token = jwt.getRefresh_token();
                access_token = jwt.getAccess_token();
                expiresIn = jwt.getExpires_in();

                jedis.hset(redisKey, "login", login);
                jedis.hset(redisKey, "UUID", UUID);
                // 登录Token，两小时有效期
                String loginToken = PasswordUtil.md5(login + new Date());
                jedis.hset(redisKey, "login_token", loginToken);
                jedis.hset(redisKey, "login_token_time", String.valueOf(System.currentTimeMillis()));
                // 登录刷新Token，expireDays天有效期
                String loginRefreshToken = PasswordUtil.md5(login + UUID + new Date());
                jedis.hset(redisKey, "login_refresh_token", loginRefreshToken);
                jedis.hset(redisKey, "access_token", access_token);
//                jedis.hset(redisKey, "refresh_token", refresh_token);
                jedis.hset(redisKey, "expires_in", String.valueOf(expiresIn));
                // 一天等于86400s
                jedis.expire(redisKey, expireDays * 86400);
                LCLogger.info().withMessageKey("getLoginToken")
                        .withMessage("getLoginToken=> loginToken: " + loginToken).flush();
                return loginToken;
            } catch (Exception e) {
                throw new Exception(e);
            } finally {
                RedisUtil.returnResource(jedis);
            }
        } else {
            return "";
        }
    }
    /**************** 自动登录token管理-end  *************/


    /**************** 微信&QQ账号关联-start*************/
    @Override
    public void thirdClientBindUser(User record) throws Exception {
        userMapper.thirdClientBindUser(record);
    }

    @Override
    public void unBoundAccount(User user) throws Exception {
        String weChat = "weChat";
        String qq = "QQ";
        if (weChat.equals(user.getClient())) {
            userMapper.unBoundWeChatAccount(user);
        } else if (qq.equals(user.getClient())) {
            userMapper.unBoundQQAccount(user);
        }
    }

    @Override
    public User getBoundAccounts(User user) throws Exception {
        return userMapper.getBoundAccounts(user);
    }

    /**************** 微信&QQ账号关联-end  *************/


    @Override
    public void updateUserInfo(User record) throws Exception {
        userMapper.updateUserInfo(record);
    }

    /**
     * 重置redis中的sessionkey的值
     *
     * @author LiQun
     * @param code            票据
     * @param appEncryptionId app加密id
     * @param session_key     自定义的key
     * @date 2019/3/28 14:44
     */
    @Override
    public JSONObject resetSessionKey(String code, String appEncryptionId, String session_key) throws Exception {
        LinkRedisClient jedis = null;
        JSONObject mpResult = null;
        try {
            jedis = RedisUtil.getJedis();
            Mp mp = getMpConfigInfo(jedis, appEncryptionId);
            String mpAppId = mp.getAppId();
            String mpAppSecret = mp.getAppSecret();
            String mpCorpId = mp.getCorpid().toString();
            if (StringUtils.isBlank(mpAppId) || StringUtils.isBlank(mpAppSecret) || StringUtils.isBlank(mpCorpId)) {
                throw new BasicServiceException("获取小程序参数信息异常!");
            }
            // 获取openId,unionid,sessionkey信息
            mpResult = getJsCodeToSession(code, mpAppId, mpAppSecret);
            if (mpResult == null) {
                throw new BasicServiceException("调用微信接口返回信息为空，请检查");
            } else {
                String errcode = "errcode";
                if (mpResult.get(errcode) != null) {
                    String errorResult = mpResult.getString("errmsg");
                    throw new BasicServiceException(errorResult);
                } else {
                    // 会话密钥
                    String sessionKey = mpResult.getString("session_key");
                    // 将redis中的值重置
                    jedis.hset(session_key, "sessionKey", sessionKey);
                    jedis.expire(session_key, 7200);
                }
            }
        } finally {
            RedisUtil.returnResource(jedis);
        }
        return mpResult;
    }

    @Override
    public RespDTO mpLogin(User user) throws Exception {
        LoginDTO loginDTO = new LoginDTO();
        LinkRedisClient j = null;
        try {
            // 小程序登陆验证票据
            String code = user.getCode();
            // 用于存放前端小程序传入的加密字段,不再额外开字段了,用于根据当前字段从配置表中获取小程序信息
            String appEncryptionId = user.getCurClientId();
            if (StringUtils.isBlank(code)) {
                throw new BasicServiceException("未获取到code");
            }

            j = RedisUtil.getJedis();
            // 从redis中获取配置信息
            Mp mp = getMpConfigInfo(j, appEncryptionId);
            String mpAppId = mp.getAppId();
            String mpAppSecret = mp.getAppSecret();
            String mpCorpId = String.valueOf(mp.getCorpid());
//
//            String mpAppId = null;
//            String mpAppSecret = null;
//            String mpCorpId = null;
//            j = RedisUtil.getJedis();
//            mpAppId = j.hget("mp_info:" + appEncryptionId, "mp_app_id");
//            mpAppSecret = j.hget("mp_info:" + appEncryptionId, "mp_app_secret");
//            mpCorpId = j.hget("mp_info:" + appEncryptionId, "mp_app_corp_id");

//            if (StringUtils.isBlank(mpAppId) || StringUtils.isBlank(mpAppSecret) || StringUtils.isBlank(mpCorpId)) {
//                // 暂时用于兼容之前的逻辑,等全面调整后删除该逻辑
//                if (StringUtils.isBlank(appEncryptionId)) {
//                    throw new Exception("请传入appEncryptionId!");
//                }
//                // 根据前端传入的加密id查询
//                Mp mp = new Mp();
//                mp.setAppEncryptionId(appEncryptionId);
//                mp = mpService.queryMpInfoByEncryptionId(mp);
//                mpAppId = mp.getAppId();
//                mpAppSecret = mp.getAppSecret();
//                mpCorpId = String.valueOf(mp.getCorpid());
//                // 将数据缓存起来
//                j.hset("mp_info:" + appEncryptionId, "mp_app_id", mpAppId);
//                j.hset("mp_info:" + appEncryptionId, "mp_app_secret", mpAppSecret);
//                j.hset("mp_info:" + appEncryptionId, "mp_app_corp_id", mpCorpId);
//            }

            if (StringUtils.isBlank(mpAppId) || StringUtils.isBlank(mpAppSecret) || StringUtils.isBlank(mpCorpId)) {
                throw new BasicServiceException("获取小程序参数信息异常!");
            }

            //根据code获取openId
            String params = "appid=" + mpAppId + "&secret="
                    + mpAppSecret + "&js_code=" + code + "&grant_type=authorization_code";
            LCLogger.info().withMessageKey("mpLogin")
                    .withMessage("传递参数 : " + params).flush();


            String resultParams = HttpUtils.sendPost("https://api.weixin.qq.com/sns/jscode2session", params);
            LCLogger.info().withMessageKey("mpLogin")
                    .withMessage("返回参数 : " + resultParams).flush();
            JSONObject mpResult = JSONObject.parseObject(resultParams);
            // 根据获取到的unionId查询消费者信息,不存在则创建
            if (mpResult == null) {
                throw new BasicServiceException("调用微信接口返回信息为空，请检查");
            } else {
                String errcode = "errcode";
                if (mpResult.get(errcode) != null) {
                    String errorResult = mpResult.getString("errmsg");
                    throw new BasicServiceException(errorResult);
                } else {
                    String mpOpenId = mpResult.getString("openid");
                    // 会话密钥
                    String sessionKey = mpResult.getString("session_key");
                    String unionId = "";
                    try {
                        // 尝试获取unionid
                        unionId = mpResult.getString("unionid");
                    } catch (Exception e) {
                        LCLogger.error().withMessageKey("mpLogin")
                                .withMessage("当前授权未获取到unionId，小程序用户【"
                                        + mpOpenId + "】未关注公众号.").flush();
                    }
                    if (StringUtils.isNotBlank(mpOpenId)) {
                        String dynamicAesKey = UUID.randomUUID().toString().replaceAll("-","").substring(0,8);
                        String session_key = "";
                        // 获取小程序登陆场景值
                        String attenWays = user.getAttenWays() == null ? "" : user.getAttenWays().toString();
                        Account account = getAccount(mp, mpOpenId, unionId, Long.valueOf(mpCorpId), null, mpAppId,attenWays);
                       // account.setDynamicAeskey(dynamicAesKey);

                        // 若消费者信息缺失unionId，则需要触发unionId机制，无论消费者是否已经存在
                       // String unionIdTemp = account.getWxUnionid();
                        /*if (StringUtils.isBlank(unionIdTemp)) {
                            LCLogger.info().withMessageKey("mpLogin")
                                    .withMessage("用户暂无unionId信息，触发unionId获取机制...").flush();
                        }*/
                        // 存储小程序会话密钥
                        session_key = PasswordUtil.md5(new Date() + mpOpenId);
                        j.hset(session_key, "openId", mpOpenId);
                        j.hset(session_key, "sessionKey", sessionKey);
                        j.expire(session_key, 7200);


                        User mpDefaultUser = new User();
                        mpDefaultUser.setId(mp.getDefaultUserId());
                        mpDefaultUser.setFirstName(mp.getDefaultUserName());
                        mpDefaultUser.setUsername(mp.getDefaultLoginUserName());
                        mpDefaultUser.setPostnId(mp.getDefaultUserPostnId());
                        mpDefaultUser.setOrgId(mp.getDefaultUserOrgId());
                        mpDefaultUser.setCorpid(Long.valueOf(mpCorpId));
                        mpDefaultUser.setPriceListId(mp.getPriceListId());



                        // 构造User对象，存入redis
                        String reqIp = user.getIpAddress();

                        user = new User();
                        user.setEmpType("MPEmployee");
                        user.setId(mpDefaultUser.getId());
                        user.setCorpid(Long.valueOf(mpCorpId));
                        user.setIpAddress(reqIp);
                        user.setAnonymousFlag("Y");
                        user.setLoginType("MP");
                        user.setLogin(String.valueOf(mpDefaultUser.getId()));
                        user.setLoginTime(DateUtil.dateToStr(new Date(), "yyyy-MM-dd HH:mm:ss"));
                        user.setUsername(mpDefaultUser.getUsername());
                        user.setAcctId(account.getId());
                        user.setOrgId(mpDefaultUser.getOrgId());
                        user.setPostnId(mpDefaultUser.getPostnId());
                        user.setMpAppId(mpAppId);
                        user.setOpenId(mpOpenId);
                       // user.setFirstName(account.getWxNickname());
                        user.setPriceListId(mpDefaultUser.getPriceListId());

                        // 用来存放小程序加密id,便于后续读取配置信息
                        user.setIntegrationId(appEncryptionId);
                        user.setWxUnionId(unionId);
                        user.setDynamicAeskey(dynamicAesKey);
                        user.setCreatedBy(account.getCreatedBy());
                        user.setLastUpdatedBy(account.getLastUpdatedBy());
//                        user.setLogin(String.valueOf(account.getCreatedBy()));
//                        user.setAnonymousFlag("Y");
//                        user.setLoginType("MP");
//                        user.setLogin(String.valueOf(account.getId()));
//                        user.setAcctId(account.getId());
//                        user.setOrgId(account.getOrgId());
//                        user.setPostnId(account.getPostnId());
//                        user.setMpAppId(mpAppId);
//                        user.setOpenId(mpOpenId);
//                        user.setFirstName(account.getWxNickname());
//                        // 用来存放小程序加密id,便于后续读取该信息

//                        user.setIntegrationId(appEncryptionId);
                        // 存储用户对象
                        JWT jwt = getAccessToken("", "", "mp",
                                "", mpCorpId, WECHAT_ENTRANCE);

                        String token = jwt.getAccess_token();
                        int tokenExpire = jwt.getExpires_in();
                        //认证成功后将登录用户信息存到redis
                        SecurityInterceptor.token.set(token);
                        //把用户放到线程里面。保证下面的操作。是基于当前登录用户。
                        if (StringUtils.isNull(user.getCurClientId())) {
                            user.setCurClientId(WECHAT_ENTRANCE);
                        }
                        SecurityInterceptor.tempUser.set(user);
                        this.setLoginInfo(token, user, "", tokenExpire, WECHAT_ENTRANCE, null);
                        String userJsonStr = JSONObject.toJSONString(user);
                        j.set(token, userJsonStr);
                        j.expire(token, tokenExpire);

                        account.setAnonymousFlag("Y");

                        // 查询关注者表,如果不存在关注者,则插入记录
                        Subscriber subscriber = wxSubscriberService.queryByOpenId(mpOpenId);
                        if (subscriber == null) {
                            subscriber = new Subscriber();
                            subscriber.setRow_status(ROW_STATUS_INSERT);
                            subscriber.setId(keyGenerateService.keyGenerate());
                            subscriber.setOpenid(mpOpenId);
                            subscriber.setUnionid(unionId);
                            subscriber.setWxAppId(mpAppId);
                            subscriber.setSource("MiniProgram");
                            subscriber.setCreatedBy(mpDefaultUser.getId());
                            subscriber.setLastUpdatedBy(mpDefaultUser.getId());
                            subscriber.setCorpid(Long.valueOf(mpCorpId));
                            subscriber.setSex(account.getGender());
                            subscriber.setCity(account.getCity());
                            subscriber.setCountry(account.getCountry());
                            subscriber.setProvince(account.getProvince());
                            wxSubscriberService.insert(subscriber);
                        }
                        // 如果wxAppId 为空 更新wxAppid
                        if (StringUtils.isBlank(subscriber.getWxAppId()) && StringUtils.isNotBlank(mpAppId)) {
                            subscriber.setWxAppId(mpAppId);
                            wxSubscriberService.updateSub(subscriber);
                        }
                        loginDTO.setSessionKey(session_key);
                        loginDTO.setAccount(account);
                        loginDTO.setToken(token);
                    } else {
                        throw new BasicServiceException("未获取到该小程序用户的openId");
                    }
                }
            }

        } catch (Exception e) {
            LCLogger.withException(e);
            throw new BasicServiceException(e.getMessage());
        } finally {
            RedisUtil.returnResource(j);
        }
        return RespDTO.onSuc(loginDTO);
    }

    @Override
    public RespDTO corpWxMpLogin(User user) throws Exception {
        LoginDTO loginDTO = new LoginDTO();
        LinkRedisClient j = null;
        try {
            // 小程序登陆验证票据
            String code = user.getCode();
            // 用于存放前端小程序传入的加密字段,不再额外开字段了,用于根据当前字段从配置表中获取小程序信息
            String appEncryptionId = user.getCurClientId();
            if (StringUtils.isBlank(code)) {
                throw new BasicServiceException("未获取到code");
            }
            String mpAppId1 = user.getMpAppId();
            if (StringUtils.isBlank(mpAppId1)) {
                throw new BasicServiceException("应用id不能为空");
            }
            JSONObject userIdSession = agentService.getSessionKey(mpAppId1,code);


            j = RedisUtil.getJedis();

            String username = userIdSession.getString("userid");
            agentService.cacheSessionKey(userIdSession);
            //查询用户，设置当前登录用户信息
            // 构造User对象，存入redis
            User example = new User();
            if (null != username) {
                user.setLogin(username);
                example.setUsername(username.toUpperCase());
            }
            user = loginMapper.queryByUsername(example);
            // 用来存放小程序加密id,便于后续读取配置信息
            user.setIntegrationId(appEncryptionId);
            User userWithPasswd = userMapper.queryByLogin(user);
            JWT jwt = getAccessToken(username, userWithPasswd.getPassword() , "",
                    "", "", CORPWX_MP_ENTRANCE);

            String token = jwt.getAccess_token();
            int tokenExpire = jwt.getExpires_in();
            //认证成功后将登录用户信息存到redis
            SecurityInterceptor.token.set(token);
            //把用户放到线程里面。保证下面的操作。是基于当前登录用户。
            if (StringUtils.isNull(user.getCurClientId())) {
                user.setCurClientId(CORPWX_MP_ENTRANCE);
            }
            String dynamicAesKey = UUID.randomUUID().toString().replaceAll("-","").substring(0,8);
            user.setDynamicAeskey(dynamicAesKey);
            SecurityInterceptor.tempUser.set(user);
            this.setLoginInfo(token, user, "", tokenExpire, CORPWX_MP_ENTRANCE, null);
            String userJsonStr = JSONObject.toJSONString(user);
            j.set(token, userJsonStr);
            j.expire(token, tokenExpire);

            loginDTO.setExpires(tokenExpire);
//            loginDTO.setSessionKey(sessionKey);
//            loginDTO.setAccount(account);
            loginDTO.setToken(token);
            loginDTO.setUser(user);
        } catch (Exception e) {
            LCLogger.withException(e);
            throw new BasicServiceException(e.getMessage());
        } finally {
            RedisUtil.returnResource(j);
        }
        return RespDTO.onSuc(loginDTO);
    }


    /**
     * 将DMP数据转化为link的对象
     *
     * @param jsonObj
     * @return com.link.business.base.accnt.model.Account
     * @author ZengChi
     * @date 2019-09-24 18:27
     */
    @Override
    public Account transformDmpToLink(JSONObject jsonObj) {
        Account obj = new Account();
        Class<Account> accountClass = Account.class;
        Field[] fields = accountClass.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(DmpField.class)) {
                // link字段名
                String name = field.getName();
                // 对应set方法
                String methodName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
                DmpField annotation = field.getAnnotation(DmpField.class);
                // dmp对应字段名，作为map的KEY
                String linkFieldName = annotation.fieldName();
                Object valueObj = jsonObj.get(linkFieldName);
                if (valueObj != null) {
                    // dmp字段值
                    String value = valueObj.toString();
                    try {
                        Method method = accountClass.getDeclaredMethod(methodName, String.class);
                        method.invoke(obj, new Object[]{value});
                    } catch (NoSuchMethodException e) {
                        throw new BasicServiceException(" 不存在【" + methodName + "】此方法");
                    } catch (IllegalAccessException e) {
                        throw new BasicServiceException("【" + methodName + "】方法执行失败");
                    } catch (InvocationTargetException e) {
                        throw new BasicServiceException("【" + methodName + "】方法执行失败");
                    }
                }
            }
        }

        String currentTime = DateUtil.dateToStr(new Date(), "yyyy-MM-dd HH:mm:ss");

        if (jsonObj.get("row_id") != null && !"".equals(jsonObj.get("row_id"))) {
            obj.setId(Long.parseLong(jsonObj.get("row_id").toString()));
        }
        if (jsonObj.get("created") != null && !"".equals(jsonObj.get("created"))) {
            obj.setCreated(jsonObj.get("created").toString());
        } else {
            obj.setCreated(currentTime);
        }
        if (jsonObj.get("created_by") != null && !"".equals(jsonObj.get("created_by"))) {
            obj.setCreatedBy(Long.parseLong(jsonObj.get("created_by").toString()));
        }
        if (jsonObj.get("last_upd") != null && !"".equals(jsonObj.get("last_upd"))) {
            obj.setLastUpdated(jsonObj.get("last_upd").toString());
        } else {
            obj.setLastUpdated(currentTime);
        }
        if (jsonObj.get("last_upd_by") != null && !"".equals(jsonObj.get("last_upd_by"))) {
            obj.setLastUpdatedBy(Long.parseLong(jsonObj.get("last_upd_by").toString()));
        }
        if (jsonObj.get("corp_id") != null && !"".equals(jsonObj.get("corp_id"))) {
            obj.setCorpid(Long.parseLong(jsonObj.get("corp_id").toString()));
        }
        if (jsonObj.get("org_id") != null && !"".equals(jsonObj.get("org_id"))) {
            obj.setOrgId(Long.parseLong(jsonObj.get("org_id").toString()));
        }
        if (jsonObj.get("postn_id") != null && !"".equals(jsonObj.get("postn_id"))) {
            obj.setPostnId(Long.parseLong(jsonObj.get("postn_id").toString()));
        }
        if (jsonObj.get("par_consumer_id") != null && !"".equals(jsonObj.get("par_consumer_id"))) {
            obj.setAcctParentId(Long.parseLong(jsonObj.get("par_consumer_id").toString()));
        } else {
            obj.setAcctParentId(null);
        }
        if ("".equals(jsonObj.get("birthday"))) {
            obj.setBirthday(null);
        }

        return obj;
    }

    /**
     * 将link中的Account对象做字段映射，提供调用DMP接口的消费者参数
     *
     * @param entity 新增客户参数
     * @return
     * @author ZengChi
     * @date 2019-09-24 17:06
     */
    @Override
    public Object transformToDmpConsumer(Account entity) {
        CoreUser user = UserUtil.getUser();
        Map<String, Object> param = new HashMap<>(100);
        // 标准字段
        param.put("row_id", entity.getId() == null ? "" : entity.getId());
        String orgId = StringUtils.isBlank(entity.getOrgId() + "") ? user.getOrgId() + "" : entity.getOrgId() + "";
        String postnId = StringUtils.isBlank(entity.getPostnId() + "") ? user.getPostnId() + "" : entity.getPostnId() + "";
        if (StringUtils.isBlank(orgId) || StringUtils.isBlank(postnId)) {
            throw new BasicServiceException("请联系管理员维护组织或职位信息");
        }

        String currentTime = DateUtil.dateToStr(new Date(), "yyyy-MM-dd HH:mm:ss");

        param.put("created", entity.getCreated() == null || "".equals(entity.getCreated()) ? currentTime : entity.getCreated());
        param.put("created_by", entity.getCreatedBy() == null ? user.getId() : entity.getCreatedBy());
        param.put("last_upd", entity.getLastUpdated() == null || "".equals(entity.getLastUpdated()) ? currentTime : entity.getLastUpdated());
        param.put("last_upd_by", entity.getLastUpdatedBy() == null ? user.getId() : entity.getLastUpdatedBy());
        param.put("corp_id", entity.getCorpid() == null ? entity.getLoginCorpId() : entity.getCorpid());
        param.put("org_id", orgId == null || "".equals(orgId) || "null".equals(orgId) ? "0" : orgId);
        param.put("postn_id", postnId == null || "".equals(postnId) || "null".equals(postnId) ? "0" : postnId);
        param.put("par_consumer_id", entity.getAcctParentId() == null ? "" : entity.getAcctParentId());

        // 已经做过映射的字段
        SecretKeyUtil.DmpParamUtil.transformDmpAnnotationField(entity, param);

        param.put("frist_time",currentTime);


        return param;
    }


    /**
     * 根据openid 获取客户信息
     *
     * @param mpOpenId 小程序openId
     * @param unionId  微信unionId
     * @return account 客户
     * @throws Exception 异常信息
     */
    private Account getAccount(Mp mp, String mpOpenId, String unionId, Long mpCorpId,
                               String accountName, String mpAppId,String atteWays) throws Exception {
        Account accountParam = new Account();
        accountParam.setAnonymousFlag("Y");
        accountParam.setLoginCorpId(mpCorpId);
        //只查询渠道消费者
        // 通过小程序openId查询消费者信息，不存在则新建
        Account account = accountService.queryConsumerByOpenId(accountParam);
        if (account == null) {
            account = new Account();
            account.setCorpid(mpCorpId);
//            account.setAcctName(accountName);
//            Enterprise enterprise = new Enterprise();
//            enterprise.setLoginCorpId(mpCorpId);
//            enterprise.setCode(mpCorpId);
//            enterprise = enterpriseService.queryByCode(enterprise);
//            User admin = new User();
//            admin.setId(enterprise.getUserId());
//            admin.setLoginCorpId(mpCorpId);
//            admin = queryById(admin);
            account.setOrgId(mp.getDefaultUserOrgId());
            account.setPostnId(mp.getDefaultUserPostnId());
            account.setLoginCorpId(mpCorpId);
            account.setAcctChannel(mpAppId);
            account.setIntegrateId(mpAppId);
            account.setAcctStatus("Y");
            accountService.addConsumer(account);
            Object object = transformToDmpConsumer(account);
            // 把消费者同步到dmp
            KafkaSender.sendInfo("CDC_CONSUMER", JsonUtil.toString(object));
        }
        return account;
    }

    /**
     * @param login    用户名
     * @param passwd   密码
     * @param type     类型 如果是小程序 则为 'mp'
     * @param mpOpenId 小程序openid
     * @param mpCorpId 小程序corpId
     * @param scope 登录入口
     * @return JWT
     * @throws BasicServiceException
     */
    @Override
    public JWT getAccessToken(String login, String passwd, String type, String mpOpenId, String mpCorpId, String scope) throws Exception {
        //获取得到认证客户端信息
        Authclient authclient = getAuthclient(scope, "");
        //获取得到认证字符串信息
        String authStr = genAuthHeader(authclient);
        //传入的密码不为空，表示当前不为第三方登录
        Map<String, String> jsonO = new HashMap<>(16);
        jsonO.put("username", login);
        jsonO.put("password", passwd);
        jsonO.put("scope", scope);
        jsonO.put("grant_type", "password");
        String mp = "mp";
        if (mp.equals(type)) {
            jsonO.put("thirdName", "admin");
            jsonO.put("thirdPasswd", "appThirdLogin");
            jsonO.put("openId", mpOpenId);
            jsonO.put("thirdType", "mp");
            jsonO.put("corpId", mpCorpId);
        }

        String retStr = authClient.getToken(authStr, jsonO);

        JSONObject json = JSONObject.parseObject(retStr);
        return JSONObject.toJavaObject(json, JWT.class);
    }

    /**
     * 这个方法主要是生成一个随机数作为password的值
     * 1.password作为传值的变量名且不能改变
     * 2.password的值是什么在后续并没有起到实际的作用
     * 3.使用这个方法绕过可能检测到的漏洞威胁
     *
     * @return
     */
    private String generateNum() {
        return String.valueOf(Math.random());
    }


    /**
     * 账号初始化密码
     *
     * @param record
     * @throws Exception
     */
    @Override
    public void genPsw(User record) throws Exception {
        User user = new User();
        user.setId(record.getId());
        user = queryById(user);
        String password = user.getContactPhone();
        if (password == null || password.length() < 6) {
            throw new BasicServiceException("联系电话错误！");
        }
        password = password.substring(password.length() - 6, password.length());
        resetPassword(user, password, password);
        int n = 0;
        setLoginErrorTimes(user.getUsername(), n);
    }


    /**
     * 设置某用户名的登录错误次数
     *
     * @param username   用户名
     * @param errorTimes 要设置的密码错误次数
     */
    @Override
    public void setLoginErrorTimes(String username, int errorTimes) throws Exception {
        LinkRedisClient jedis = null;
        try {
            jedis = RedisUtil.getJedis();
            String redisKey = "crm_loginError_info";
            String currentdate = DateUtil.dateToStr(new Date(), "yyyy-MM-dd HH:mm:ss");
            if (jedis.exists(redisKey) && jedis.hmget(redisKey, username).size() > 0) {
                Map<String, String> map = new HashMap<String, String>();
                map.put(username, errorTimes + "+" + currentdate);
                jedis.hdel(redisKey, username);
                jedis.hmset(redisKey, map);
            }
        } finally {
            RedisUtil.returnResource(jedis);
        }
    }


    /**
     * 更新用户状态
     *
     * @param record
     * @throws Exception
     */
    @Override
    @Transactional(rollbackFor = BasicServiceException.class)
    public void userStatusUpdate(User record) throws Exception {
        userMapper.userStatusUpdate(record);
    }

    /**
     * 根据corpid 查询User
     *
     * @param corpId
     * @return user
     * @throws Exception
     * @author wangdawei
     */
    @Override
    public User queryUserByCropId(Long corpId) throws Exception {
        if (corpId == null ) {
            corpId = 0L;
        }
        Enterprise enterprise = new Enterprise();
        enterprise.setLoginCorpId(corpId);
        enterprise.setCode(corpId);
        enterprise = enterpriseService.queryByCode(enterprise);
        User admin = new User();
        admin.setUsername(admin.getUsername());
        admin.setId(enterprise.getUserId());
        admin.setLoginCorpId(corpId);
        admin.setCorpid(corpId);
        admin.setUsername(enterprise.getUserName());
        admin = queryEnterpriseAdminByName(admin);
        return admin;
    }

    @Override
    public User queryEnterpriseAdminByName(User user) throws Exception {
        return userMapper.queryEnterpriseAdminByName(user);
    }

    /**
     * 快速设置员工组织、职位、职责
     *
     * @param entity 员工信息
     * @throws Exception
     * @author 戴晨雨
     * @date 2019/2/26
     */
    @Override
    public void beforUpsert(User entity) throws Exception {
        super.beforUpsert(entity);
        if (entity.getId() == null) {
            entity.setId(keyGenerateService.keyGenerate());
        }

        if (StringUtils.isNotBlank(entity.getOrganCode())) {
            //组织编码有值，查询组织
            QueryParams qps = new QueryParams();
            qps.addFilter(new Filter("`orgCode`", Filter.OP_EQUAL, entity.getOrganCode()));
            Orgnization org = new Orgnization();
            Orgnization queryOrg = (Orgnization) Orgnization.transformClass(org, qps);
            List<Orgnization> orgList = orgnizationService.queryByExamplePage(queryOrg);
            if (orgList == null || orgList.size() < 1) {
                throw new BasicServiceException("输入的组织编码未找到对应组织。");
            }
            //设置用户组织字段改为该值
            entity.setOrgCode(String.valueOf(orgList.get(0).getId()));
        }

        if (StringUtils.isNotBlank(entity.getPostnCode())) {
            //查询出对应职位
            QueryParams qps = new QueryParams();
            qps.setSearchParam("integrationId");
            qps.setSearchValue(entity.getPostnCode());
            Position position = new Position();
            Position queryPos = (Position) Position.transformClass(position, qps);
            List<Position> posList = positionService.queryByExamplePage(queryPos);
            if (posList == null || posList.size() < 1) {
                throw new BasicServiceException("输入的职位编码未找到对应职位。");
            }
            //职位编码有值，用户主要职位设置为该值
            Long postnId = posList.get(0).getId();
            entity.setPostnId(postnId);
            Position posParam = new Position();
            posParam.setUserId(entity.getId());
            List<Position> positions = positionService.queryByUserPage(posParam);
            Mvg mvg = new Mvg();
            mvg.setMvgParentId(entity.getId());
            //删除mvg中其他职位
            int i = 0;
            for (Position p : positions) {
                if (!StringUtils.equals(String.valueOf(p.getId()), String.valueOf(postnId))) {
                    mvg.setMvgSubsetId(p.getId());
                    userPostnMvgMapper.deleteById(mvg);
                    i++;
                }
            }
            //将该值加入到mvg中
            if (i == positions.size() || positions.size() == 0) {
                mvg.setId(keyGenerateService.keyGenerate());
                mvg.setMvgParentId(entity.getId());
                mvg.setMvgSubsetId(postnId);
                userPostnMvgMapper.insert(mvg);
            }
        }

        if (StringUtils.isNotBlank(entity.getDutyCode())) {
            //查询出对应职责
            QueryParams qps = new QueryParams();
            qps.setSearchParam("name");
            qps.setSearchValue(entity.getDutyCode());
            Duty d = new Duty();
            Duty queryDuty = (Duty) Duty.transformClass(d, qps);
            List<Duty> dutyList = dutyService.queryByExamplePage(queryDuty);
            if (dutyList == null || dutyList.size() < 1) {
                throw new BasicServiceException("输入的职责名称未找到对应职责。");
            }
            Long dutyId = dutyList.get(0).getId();
            //职责编码有值，用户主要职责改为该值
            entity.setDutyId(dutyId);
            Mvg mvg = new Mvg();
            mvg.setMvgParentId(entity.getId());
            mvg.setLoginCorpId(entity.getLoginCorpId());
            //删除mvg中其他职责
            userDutyMvgMapper.deleteAllById(mvg);
            //将该值加入到mvg中
            mvg.setId(keyGenerateService.keyGenerate());
            mvg.setMvgSubsetId(dutyId);
            userDutyMvgMapper.insert(mvg);
        }
    }

    @Override
    public List<User> selectByOrgIdPage(User user) throws Exception {
        return userMapper.selectByOrgIdPage(user);
    }

    @Override
    public User queryByLogin(User user) throws Exception {
        return userMapper.queryByLogin(user);
    }

    /**
     *
     *
     * @author huanglongjian
     * @date 2019-08-06 19:55
     * @param usernameArg 用户名
     * @param password 密码
     * @param loginType 登录类型。分为五种，crm常规登录 crmLogin，api登录 apiLogin
     *                  dms常规登录dmsLogin, crm三方登录crmThird，dms三方登录dmsThird
     * @param request 请求request参数
     * @param language
     * @return com.link.business.base.user.model.RespDTO
     */
    @Override
    public RespDTO login(String usernameArg, String password, String loginType, HttpServletRequest request, String language) throws Exception {
        if (StringUtils.isNull(usernameArg)) {
            throw new BasicServiceException("用户名不能为空");
        }

        //把用户名大写传给一个变量
        String username = usernameArg.toUpperCase();
        //登录的范围
        String scope;
        //第三方标识
        boolean thirdFlag = false;
        if (StringUtils.isBlank(language)) {
            language = "zh-CN";
        }

        //认证额外参数 不给值
        Map<String, String> extraParamMap = new HashMap<>(16);

        //设置登录的范围
        if ("crmLogin".equals(loginType) || "crmThird".equals(loginType)) {
            //如果是crm常规登录和crm三方登录，则scope为 crm
            scope = CRM_ENTRANCE;
        } else if ("dmsLogin".equals(loginType) || "dmsThird".equals(loginType)) {
            scope = DMS_ENTRANCE;
        } else if ("oauthTokenLogin".equals(loginType)) {
            scope = CRM_ENTRANCE;
        } else {
            throw new Exception("登录类型错误！");
        }

        if ("crmThird".equals(loginType) || "dmsThird".equals(loginType)) {
            //第三方登录模式，设置需要的额外参数
            extraParamMap.put("openId", "");
            extraParamMap.put("thirdType", "mp");
            thirdFlag = true;
        } else if ("oauthTokenLogin".equals(loginType)) {
            extraParamMap.put("openId", username);
            extraParamMap.put("thirdType", "oauth_token");
            thirdFlag = true;
        }
        //通过范围查询得到客户端
        Authclient authclient = getAuthclient(scope, "");
        //校验用户信息
        User user = checkUser(username, scope, request, authclient);
        user.setLoginLanguage(language);

        // 密码校验策略.包括正确性，是否过期，密码强度等(目前只校验crm用户)
        long checkPasswordStartTime = System.currentTimeMillis();
        // 查询企业信息
        Enterprise enterprise = enterpriseService.queryByCode(user.getCorpid());
        if (!thirdFlag) {
            RespDTO respDTO = checkPassword(user, password, enterprise);
            long checkPasswordEndTime = System.currentTimeMillis();
            logger.error("login:checkPassword:" + (FunctionTimeUtil.div((checkPasswordEndTime - checkPasswordStartTime), 1000, 3)));
            if (respDTO != null) {
                return respDTO;
            }
        } else if ("oauthTokenLogin".equals(loginType)) {
            // 认证登录直接通过
        } else {
            String[] authInfo = password.split("_");
            if (authInfo.length > 2) {
                String unionId = authInfo[0];
                String platformType = authInfo[1];
                if ("QQ".equals(platformType) && !unionId.equals(user.getWxUnionId())) {
                    throw new BasicServiceException("当前用户未绑定QQ");
                }
                if ("WX".equals(platformType) && !unionId.equals(user.getWxUnionId())) {
                    throw new BasicServiceException("当前用户未绑定微信");
                }
            }
        }

        //到认证服务器校验密码
        LoginDTO loginDTO = checkPasswordFromAuth(authclient, username, password, thirdFlag, extraParamMap,false);
        loginDTO.setUser(user);

        //后置操作,包括把相关信息写入到线程，设置报表相关的token
        afterLogin(loginDTO, user, authclient, enterprise);
        return RespDTO.onSuc(loginDTO);
    }


    /**
     * 校验密码，返回校验的得到的json对象
     *
     * @author huanglongjian
     * @date 2019-08-06 18:55
     * @param authclient oauth客户端对象
     * @param extraParamMap 额外的参数
     * @param username 用户名
     * @param password 密码
     * @return com.alibaba.fastjson.JSONObject
     */
    private LoginDTO checkPasswordFromAuth(Authclient authclient, String username, String password,
                                           boolean thirdFlag, Map<String, String> extraParamMap,boolean apiLogin) throws Exception {
        long starttime = System.currentTimeMillis();
        //先校验校验失败次数
        checkLoginErrorTimes(username);

        //获取认证头信息
        String authAuthorization;
        if (apiLogin) {
            authAuthorization = genAuthHeader(authclient,password);
        } else {
            authAuthorization = genAuthHeader(authclient);
        }

        //获取认证的参数信息
        Map<String, String> authParam =  getAuthParam(username, password, authclient, thirdFlag, extraParamMap);

        //登录，到认证服务器获取token
        String retStr;
        try {
            retStr = authClient.getToken(authAuthorization, authParam);
        } catch (FeignException e) {
            int status = e.status();
            if (status == HttpStatus.BAD_REQUEST.value() || status == HttpStatus.UNAUTHORIZED.value()) {
                //登录错误次数+1！
                setLoginErrorTimes(username, getLoginErrorTimes(username) + 1);
                throw new BasicServiceException("获取token报错，错误代码：" + status);
            }
            throw new BasicServiceException("登录报错，请联系管理员！");
        } catch (Exception e2) {
            logger.info("获取token报错！");
            throw new Exception("登录报错，请联系管理员！");
        }
        //登录成功之后，登录错误次数清空！
        setLoginErrorTimes(username, 0);
        //获取返回的信息
        JWT jwt = JSONObject.parseObject(retStr, JWT.class);
        int expires = jwt.getExpires_in();
        LoginDTO loginDTO = new LoginDTO();
        loginDTO.setExpires(expires);
        loginDTO.setToken(jwt.getAccess_token());
        String refreshToken = jwt.getRefresh_token();
        loginDTO.setRefresh_token(refreshToken);
        long endtime = System.currentTimeMillis();
        logger.error("login:checkPasswordFromAuth:" + (FunctionTimeUtil.div((endtime - starttime), 1000, 3)));
        return loginDTO;
    }

    /**
     * 登录完成之后，做一些后置操作
     * 1、把token信息设置到线程中去
     * 2、设置页面报表可用的token
     *
     * @author huanglongjian
     * @date 2019-08-06 19:42
     * @param loginDTO        登录得到的jwt对象
     * @param authclient 登录的认证客户端信息
     */
    private void afterLogin(LoginDTO loginDTO, User user, Authclient authclient, Enterprise enterprise) throws Exception {
        long starttime = System.currentTimeMillis();
        //认证成功后将登录用户信息存到redis
        String token = loginDTO.getToken();
        SecurityInterceptor.token.set(token);
        //给用户绑定一个属性，用于superset验证
        try {
            bindingUserAndToken(user, token, loginDTO.getExpires());
        } catch (Exception e) {
            LCLogger.error().withMessageKey("login")
                    .withMessage(e + ":" + "获取user_token异常").flush();
        }
        //把clientId写入
        user.setCurClientId(authclient.getClientId());
        user.setUserToken(token);
        //把用户放到线程里面。保证下面的操作。是基于当前登录用户。
        SecurityInterceptor.tempUser.set(user);
        setLoginInfo(SecurityInterceptor.token.get(), user, loginDTO.getRefresh_token(),
                loginDTO.getExpires(), authclient.getScope(), enterprise);

        //把登录信息写入到日志表
        loginService.lastLoginDateUpdate(user);
        long endtime = System.currentTimeMillis();
        logger.error("login:afterLogin:" + (FunctionTimeUtil.div((endtime - starttime), 1000, 3)));
    }


    /**
     * 通过返回，或者clientid获得客户端信息
     *
     * @author huanglongjian
     * @date 2019-07-19 10:41
     * @param scope 范围
     * @param clientId 客户端Id
     * @return com.link.business.base.auth.model.Authclient
     */
    @Override
    public Authclient getAuthclient(String scope, String clientId) throws Exception {
        long starttime = System.currentTimeMillis();
        Authclient authclientQuery = new Authclient();
        //设置查询参数 api的特殊处理
        if (API_ENTRANCE.equals(scope) && StringUtils.isNull(clientId)) {
            throw new Exception("查询api客户端，clientid不能为空！");
        } else if (API_ENTRANCE.equals(scope) && !StringUtils.isNull(clientId)) {
            authclientQuery.setClientId(clientId);
        } else if (StringUtils.isNull(scope)) {
            throw new Exception("scope不能为空！");
        } else {
            authclientQuery.setScope(scope);
            authclientQuery.setClientId("");
        }

        //查询客户端
        List<Authclient> retList = authclientService.queryByExamplePage(authclientQuery);
        if (retList.size() < 1) {
            throw new Exception("客户端不存在！");
        }

        if (retList.size() > 1 && API_ENTRANCE.equals(scope)) {
            throw new Exception("查询到多个客户端！");
        }
        long endtime = System.currentTimeMillis();

        logger.error("login:getAuthclient:" + (FunctionTimeUtil.div((endtime - starttime), 1000, 3)));
        return retList.get(0);
    }

    /**
     * 生成到认证服务器认证的字符串
     *
     * @author huanglongjian
     * @date 2019-08-06 19:59
     * @param authlient 认证客户端信息
     * @return java.lang.String
     */
    @Override
    public String genAuthHeader(Authclient authlient) throws Exception {
        if (authlient.getLockFlag() != null && "Y".equals(authlient.getLockFlag())) {
            throw new Exception("客户端【" + authlient.getClientId() + "】已经被锁定，不能登录！");
        }

        String secret = authlient.getClientSecret();
        if (StringUtils.isNull(secret)) {
            throw new Exception("客户端【" + authlient.getClientId() + "】秘钥不能为空，请联系管理员！");
        }

        String clientId = authlient.getClientId();
        return "Basic ".concat(Base64.getEncoder().encodeToString((clientId + ":" + secret).getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 生成到认证服务器认证的字符串,api登录时使用
     *
     * @author guoshan
     * @date 2019-08-06 19:59
     * @param authlient 认证客户端信息
     * @return java.lang.String
     */
    public String genAuthHeader(Authclient authlient, String password) throws Exception {
        if (authlient.getLockFlag() != null && "Y".equals(authlient.getLockFlag())) {
            throw new Exception("客户端【" + authlient.getClientId() + "】已经被锁定，不能登录！");
        }

        if (StringUtils.isNull(password)) {
            throw new Exception("客户端【" + authlient.getClientId() + "】秘钥不能为空，请联系管理员！");
        }

        String clientId = authlient.getClientId();
        return "Basic ".concat(Base64.getEncoder().encodeToString((clientId + ":" + password).getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * api登录
     *
     * @author huanglongjian
     * @date 2019-08-06 18:35
     * @param clientId 客户端id
     * @param password 密码
     * @param request 请求参数
     * @return com.link.business.base.user.model.RespDTO
     */
    @Override
    public RespDTO apiLogin(String clientId, String password,String usernameArg, HttpServletRequest request) throws Exception {
        if (StringUtils.isNull(clientId)) {
            throw new BasicServiceException("用户名不能为空");
        }
        //登录的范围
        String scope = API_ENTRANCE;

        //通过范围查询得到客户端
        Authclient authclient = getAuthclient(scope, clientId);
        String username;
        if (StringUtils.isBlank(usernameArg)) {
            username = authclient.getUserName();
        } else {
            username = usernameArg;
        }
        if (StringUtils.isBlank(username)) {
            throw new Exception("当前客户端没有维护默认登录用户，请联系管理员！");
        }
        //校验用户信息
        User user = checkUser(username, scope, request, authclient);

        //认证额外参数 不给值
        Map<String, String> extraParamMap = new HashMap<>(16);
        //第三方标识
        boolean thirdFlag = false;
        //到认证服务器校验密码
        LoginDTO loginDTO = checkPasswordFromAuth(authclient, username, password, thirdFlag, extraParamMap,true);
        loginDTO.setUser(user);

        //后置操作,包括把相关信息写入到线程，设置报表相关的token
        afterLogin(loginDTO, user, authclient, null);
        return RespDTO.onSuc(loginDTO);
    }

    /**
     * 登录以前校验用户信息
     * @author huanglongjian
     * @date 2019-08-06 20:28
     * @param username 用户名
     * @param scope    登录范围
     * @return com.link.business.base.user.model.User
     */
    private User checkUser(String username, String scope, HttpServletRequest request, Authclient authclient) throws Exception {
        long starttime = System.currentTimeMillis();
        User user = loginService.getUser(username, null);
        if (user == null) {
            throw new BasicServiceException("用户名或密码错误，请重新输入");
        }
        String empType = user.getEmpType();
        //dms登录
        String loginType;
        if (DMS_ENTRANCE.equals(scope)) {
            //dms入口
            if (!DEALER_EMP.equals(empType) && !TERM_EMP.equals(empType)) {
                //如果dms入口，但是当前的用户不是门店用户或者经销商用户，则报错
                throw new BasicServiceException("当前用户不为经销商客户或门店客户，不能登录！");
            }
            loginType = "DMS-PC";
        } else if (CRM_ENTRANCE.equals(scope)) {
            //crm入口
            if (!VEN_EMP.equals(empType)) {
                //如果dms入口，但是当前的用户不是门店用户或者经销商用户，则报错
                throw new BasicServiceException("当前用户不为品牌商客户，不能登录！");
            }
            loginType = "CRM-PC";
        } else if (API_ENTRANCE.equals(scope)) {
            loginType = "API-PC";
        } else {
            throw new Exception("登录的范围出错！");
        }
        //设置用户里面的一些额外信息
        //登录时间
        user.setLoginTime(DateUtil.dateToStr(new Date(), "yyyy-MM-dd HH:mm:ss"));
        //ip地址
        user.setIpAddress(IpUtil.getIpAddress(request));
        //设置clientId
        user.setCurClientId(authclient.getClientId());
        user.setLoginType(loginType);
        long endtime = System.currentTimeMillis();
        logger.error("login:checkUser:" + (FunctionTimeUtil.div((endtime - starttime), 1000, 3)));
        return user;
    }

    /**
     * 构造到认证服务器认证的参数
     *
     * @author huanglongjian
     * @date 2019-08-06 21:21
     * @param username 用户名
     * @param password 密码
     * @param authclient 认证客户端
     * @param thirdFlag 第三方登录标识
     * @return java.util.Map
     */
    private Map<String, String> getAuthParam(String username, String password, Authclient authclient,
                                             boolean thirdFlag, Map<String, String> extraParamMap) {
        //构造登录验证参数
        Map<String, String> authParam = new HashMap<>();
        authParam.put("username", username);
        authParam.put("password", password);
        authParam.put("scope", authclient.getScope());
        authParam.put("grant_type", "password");

        if (thirdFlag || API_ENTRANCE.equals(authclient.getScope())) {
            //如果是第三方登录，则设置第三方登录的参数
            authParam.put("thirdName", "admin");
            authParam.put("thirdPasswd", "appThirdLogin");
        }

        //额外增加的参数
        for (Map.Entry<String, String> map : extraParamMap.entrySet()) {
            authParam.put(map.getKey(), map.getValue());
        }
        return authParam;
    }

    /**
     * pc端第三方登录
     *
     * @author huanglongjian
     * @date 2019-08-06 18:35
     * @param usernameArg 用户名
     * @param password 密码
     * @param request 请求参数
     * @param loginType 登录类型
     * @return com.link.business.base.user.model.RespDTO
     */
    @Override
    public RespDTO thirdPCLogin(String usernameArg, String password, String loginType, HttpServletRequest request) throws Exception {
        if (StringUtils.isNull(usernameArg)) {
            throw new BasicServiceException("用户名不能为空");
        }

        //把用户名大写传给一个变量
        String username = usernameArg.toUpperCase();

        String scope;
        if ("crmThird".equals(loginType)) {
            scope = CRM_ENTRANCE;
        } else if ("dmsThird".equals(loginType)) {
            scope = DMS_ENTRANCE;
        } else {
            throw new Exception("登录范围出错！");
        }
        //通过范围查询得到客户端
        Authclient authclient = getAuthclient(scope, "");
        //校验用户信息
        User user = checkUser(username, scope, request, authclient);

        String[] authInfo = password.split("_");
        if (authInfo.length > 2) {
            String unionId = authInfo[0];
            String platformType = authInfo[1];
            if ("QQ".equals(platformType) && !unionId.equals(user.getWxUnionId())) {
                throw new BasicServiceException("当前用户未绑定QQ");
            }
            if ("WX".equals(platformType) && !unionId.equals(user.getWxUnionId())) {
                throw new BasicServiceException("当前用户未绑定微信");
            }
        }

        //调用login方法进行登录
        return login(username, password, loginType, request, "");
    }

    @Override
    public List<User> queryUsersBySource(User source) {
        if (Objects.isNull(source)) {
            logger.error("queryUsersBySource：查询的来源为空");
            throw new ServiceException("");
        }

        return userMapper.queryESBUsersByUserPage(source);
    }

    @Override
    public RespDTO dingdingLogin(String authCode, String crmLogin, HttpServletRequest request) throws Exception {
        if (StringUtil.isBlank(authCode)) {
            throw new BasicServiceException("用户授权码不能为空");
        }
        //获取钉钉的token
        String accessToken = DingDingUtil.getToken();
        //获取钉钉的userId
        JSONObject userObject = DingDingUtil.getUserIdV2(accessToken,authCode);
        String userId = userObject.getString("userid");
        //获取钉钉的用户信息
        JSONObject userInfo = DingDingUtil.getUserDetail(accessToken,userId,"zh_CN");
        String mobilePhone = userInfo.getString("mobile");
        //校验link用户数据
        RespDTO respDTO = checkDingDingUser(mobilePhone,request);
        return respDTO;
    }

    private RespDTO checkDingDingUser(String mobilePhone, HttpServletRequest request) throws Exception {
        User queryOne = new User();
        queryOne.setContactPhone(mobilePhone);
        User user = loginMapper.queryByUserPhone(queryOne);
        Enterprise enterprise = enterpriseService.queryByCode(user.getCorpid());
        SecurityInterceptor.tempUser.set(user);
        SecurityInterceptor.tempEnterpirse.set(enterprise);
        RespDTO  respDTO = thirdPCLogin(user.getUsername(),"","crmThird",request);

        /*LoginDTO loginDTO = new LoginDTO();
        String token = jwt.getAccess_token();
        loginDTO.setToken(token);
        user.setUserToken(token);
        loginDTO.setUser(user);
        int tokenExpire = jwt.getExpires_in();
        //认证成功后将登录用户信息存到redis
        String userJsonStr = JSONObject.toJSONString(user);
        SecurityInterceptor.token.set(token);
        LinkRedisClient j = null;
        try {
            j = RedisUtil.getJedis();
            j.set(token, userJsonStr);
            j.expire(token, tokenExpire);
        } catch (Exception e) {
            logger.error(e.getMessage());
        } finally {
            RedisUtil.returnResource(j);
        }
        loginDTO.setExpires(tokenExpire);*/
        return respDTO;
    }

}
